using System;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace XFramework
{
    public class CodeGenPipeline : ScriptableObject
    {
        private static CodeGenPipeline I;

        private static readonly Lazy<string> Dir = new Lazy<string>(() => "Assets/Scripts/CodeGen");
        private const string FileName = "Pipeline.asset";

        [SerializeField] public CodeGenTask CurrentTask;

        public static CodeGenPipeline Default
        {
            get
            {
                if (null != I) return I;

                var filePath = $"{Dir.Value}/{FileName}";
                if (File.Exists(filePath))
                {
                    return I = AssetDatabase.LoadAssetAtPath<CodeGenPipeline>(filePath);
                }

                return I = CreateInstance<CodeGenPipeline>();
            }
        }

        public void Save()
        {
            var filePath = $"{Dir.Value}/{FileName}";
            if (!File.Exists(filePath))
            {
                AssetDatabase.CreateAsset(this, filePath);
            }

            EditorUtility.SetDirty(this);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        public void Generate(CodeGenTask task)
        {
            CurrentTask = task;

            // var codeController = task.GameObject.GetComponent<CodeController>();

            CurrentTask.Status = TaskStatus.Gen;

            var writer = new StringBuilder();
            writer.AppendLine("using UnityEngine;");
            // writer.AppendLine("using HotUpdate.Manager;");
            writer.AppendLine();

            var tab = "";
            if (!string.IsNullOrEmpty(task.Namespace))
            {
                writer.AppendLine($"namespace {task.Namespace}");
                writer.AppendLine("{");
                tab = "\t";
            }

            writer.AppendLine($"{tab}public partial class {task.ClassName} : {task.BaseName}");
            writer.AppendLine($"{tab}{{");

            writer.AppendLine($"{tab}\tvoid Start()");
            writer.AppendLine($"{tab}\t{{");
            writer.AppendLine($"{tab}\t\t// Code Here");
            writer.AppendLine($"{tab}\t}}");
            writer.AppendLine($"{tab}}}");
            if (!string.IsNullOrEmpty(task.Namespace))
            {
                writer.AppendLine("}");
            }

            task.MainCode = writer.ToString();
            writer.Clear();

            writer.AppendLine($"// Generate Id:{Guid.NewGuid().ToString()}");
            writer.AppendLine("using UnityEngine;");
            writer.AppendLine();

            tab = "";
            if (!string.IsNullOrEmpty(task.Namespace))
            {
                writer.AppendLine($"namespace {task.Namespace}");
                writer.AppendLine("{");
                tab = "\t";
            }

            writer.AppendLine($"{tab}public partial class {task.ClassName}");
            writer.AppendLine($"{tab}{{");

            // 绑定对象
            var referenceBinds = task.GameObject.GetComponent<Binds>();
            if (null != referenceBinds)
            {
                foreach (var referenceBind in referenceBinds.binds)
                {
                    writer.AppendLine($"{tab}\tpublic {referenceBind.Object.GetType().FullName} {referenceBind.Name};");
                }
            }

            writer.AppendLine($"{tab}}}");

            if (!string.IsNullOrEmpty(task.Namespace))
            {
                writer.AppendLine("}");
            }

            task.DesignerCode = writer.ToString();
            writer.Clear();

            var scriptFile = $"{task.ScriptsFolder}/{task.ClassName}.cs";
            if (!File.Exists(scriptFile))
            {
                scriptFile.GetFolderPath().CreateDirIfNotExists();
                File.WriteAllText(scriptFile, task.MainCode);
            }

            scriptFile = $"{task.ScriptsFolder}/{task.ClassName}.Designer.cs";
            File.WriteAllText(scriptFile, task.DesignerCode);

            Save();

            CurrentTask.Status = TaskStatus.Compile;
        }

        private void OnCompile()
        {
            if (null == CurrentTask) return;

            if (CurrentTask.Status == TaskStatus.Compile)
            {
                var genClassName = CurrentTask.ClassName;
                var genNamespace = CurrentTask.Namespace;

                var tpName = $"{genNamespace}.{genClassName}";

                var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(assembly =>
                    !assembly.FullName.StartsWith("Unity"));

                var type = assemblies.Where(a => a.GetType(tpName) != null)
                    .Select(a => a.GetType(tpName)).FirstOrDefault();

                if (null == type)
                {
                    Debug.Log("编译失败");
                    return;
                }

                Debug.Log(type);

                var gObj = CurrentTask.GameObject;

                var scriptCom = gObj.GetComponent(type);
                if (null == scriptCom)
                {
                    scriptCom = gObj.AddComponent(type);
                }

                var serializedObject = new SerializedObject(scriptCom);

                var referenceBinds = gObj.GetComponent<Binds>();
                if (null != referenceBinds)
                {
                    foreach (var bind in referenceBinds.binds)
                    {
                        var serializedProperty = serializedObject.FindProperty(bind.Name);
                        serializedProperty.objectReferenceValue = bind.Object;
                    }
                }

                serializedObject.ApplyModifiedProperties();
                serializedObject.UpdateIfRequiredOrScript();

                var codeGenInfo = gObj.GetComponent<CodeController>();

                var generatePrefab = codeGenInfo.GeneratePrefab;

                if (generatePrefab)
                {
                    var prefabFolder = codeGenInfo.PrefabFolder;
                    prefabFolder.CreateDirIfNotExists();

                    var genPrefabPath = $"{prefabFolder}/{gObj.name}.prefab";

                    if (!File.Exists(genPrefabPath))
                    {
                        PrefabUtility.SaveAsPrefabAssetAndConnect(gObj, genPrefabPath, InteractionMode.AutomatedAction);
                    }
                }

                EditorUtility.SetDirty(gObj);

                CurrentTask.Status = TaskStatus.Complete;
                CurrentTask = null;
            }
        }

        [DidReloadScripts]
        static void Compile()
        {
            Default.OnCompile();
        }
    }
}